#include "userAPI.h"
#include "params.h"
#include <list>

using namespace AI9;

LandCity *UserAPI::GetNearestMyCity(Position start)
{
	UserAPI &api = *this;
	int min_dist = INFINITY;
	LandCity *ret = 0;
	for (int i = 0; i < api.all_cities.size(); i ++)
		if (api.all_cities[i]->owner == api.get_my_id()) {
			Position t = api.GetCityCenter(api.all_cities[i]);
			if (api.HamDistance(start, t) < min_dist) {
				min_dist = api.HamDistance(start, t);
				ret = api.all_cities[i];
			}
		}

	return ret;
}

bool UserAPI::Recruit(vector<ActionBase*>& ret, int amount, SoldierType type)
{
	ActionRecruit *ar = new ActionRecruit();
	ar->game = game;
	ar->source = GetCntArmy();
	ar->soldierType = type;
	ar->numSoldier = amount; 
	ret.push_back(ar);
	return true;
}

UserAPI::UserAPI(GameInfo* game, int user_id) {
	this->game = game;
	this->user_id = user_id;
	my_army.clear();
	op_army.clear();
	for (int i = 0; i < game->players[user_id].numArmies; i ++)
		my_army.push_back(game->players[user_id].armies[i]);
	for (int i = 0; i < game->players[1 - user_id].numArmies; i ++)
		op_army.push_back(game->players[1 - user_id].armies[i]);

	for (int i = 0; i < game->map->sizeX; i ++)
		for (int j = 0; j < game->map->sizeY; j ++) 
			if (game->map->map[i][j]->type == LAND_CITY) {
				bool mark = false;
				for (int k = 0; k < all_cities.size(); k ++) {
					if (all_cities[k] == game->map->map[i][j]) {
						mark = true;
						break;
					}
				}
				if (!mark) all_cities.push_back(GetCity(Position(i, j)));
			}
}

int UserAPI::HamDistance(Position u, Position v) {
	return ABS(u.x - v.x) + ABS(u.y - v.y);
}

bool UserAPI::CheckPosition(Position u) {
	int width = game->map->sizeX;
	int height = game->map->sizeY;
	if (!(0 <= u.x && u.x < width && 0 <= u.y && u.y < height)) return false;
	for (int i = 0; i < my_army.size(); i ++)
		if (my_army[i] != GetCntArmy() && u == my_army[i]->pos) return false;
	for (int i = 0; i < op_army.size(); i ++)
		if (u == op_army[i]->pos) return false;

	if (game->map->map[u.x][u.y]->type == LAND_ROCK) return false;
	if (game->map->map[u.x][u.y]->type == LAND_CITY) {
		LandCity* city = GetCity(u);
		if (city->owner == 1 - user_id && city->HP > 0) return false;
	}
	return true;
}

bool UserAPI::CheckPassable(Position u, Direction d) {
	Position v(u.x + dx[d], u.y + dy[d]);
	if (!CheckPosition(u) || !CheckPosition(v)) return false;
	if (game->map->map[v.x][v.y]->type == LAND_ROCK) return false;
	if (game->map->map[v.x][v.y]->type == LAND_CITY) {
		LandCity* city = GetCity(v);
		if (city->owner == 1 - user_id && city->HP > 0) return false;
	}
	if ((game->map->map[u.x][u.y]->type == LAND_CITY) ^ (game->map->map[v.x][v.y]->type == LAND_CITY)) {
		Position incity = (game->map->map[u.x][u.y]->type == LAND_CITY) ? u : v;
		LandCity* city = GetCity(incity);
		Position center = GetCityCenter(city);
		if (incity.x != center.x && incity.y != center.y) return false;
	}
	return true;
}

int UserAPI::MoveCost(LandBase* land, Army* army) {
	if (land->type == LAND_ROCK) return INFINITY;
	Soldier soldier(army->soldierType);
	land->affect(soldier);
	int cost = soldier.speed;
	if (army->buff.find(AURA_RAISE_MOVE) != army->buff.end())
		cost -= AURA_PARAMETER[AURA_RAISE_MOVE];
	return cost;
}

void UserAPI::Bfs(Position starting) {
	int width = game->map->sizeX;
	int height = game->map->sizeY;
	for (int i = 0; i < width; i ++)
		for (int j = 0; j < height; j ++) 
			dist[i][j] = INFINITY;

	dist[starting.x][starting.y] = 0;
	prev[starting.x][starting.y] = Position(-1, -1);

	list<Position> queue;
	queue.push_back(starting);
	
	while (queue.size() > 0) {
		Position cnt_pos = queue.front();
		queue.pop_front();
		for (int k = 0; k < 4; k ++) {
			Position new_pos(cnt_pos.x + dx[k], cnt_pos.y + dy[k]);
			if (CheckPassable(cnt_pos, (Direction)(k))) {
				int cost = MoveCost(game->map->map[cnt_pos.x][cnt_pos.y], GetCntArmy());
				if (dist[new_pos.x][new_pos.y] > dist[cnt_pos.x][cnt_pos.y] + cost) {
					dist[new_pos.x][new_pos.y] = dist[cnt_pos.x][cnt_pos.y] + cost;
					prev[new_pos.x][new_pos.y] = cnt_pos;
					queue.push_back(new_pos);
				}
			}
		}
	}
}

Army* UserAPI::GetCntArmy() {
	return game->actionQueue.front();
}

LandCity* UserAPI::GetCity(Position pos) {
	LandBase* land = game->map->map[pos.x][pos.y];
	if (land->type == LAND_CITY) return (dynamic_cast<LandCity*>(land));
	return NULL;
}

Position UserAPI::GetCityCenter(LandCity* city) {
	int x = 0, y = 0;
	for (int i = 0; i < game->map->sizeX; i ++)
		for (int j = 0; j < game->map->sizeY; j ++)
			if (game->map->map[i][j] == city) {
				x += i;
				y += j;
			}
	x /= 9;
	y /= 9;
	return Position(x, y);
}

bool UserAPI::Move(vector<ActionBase*>& ret, Position starting, Position destination) {
	for (int i = 0; i < 3; ++i)
		if (MoveToRegion(ret, starting, destination, i))
			return true;
	return false;
}

bool UserAPI::MoveToRegion(vector<ActionBase*>& ret, Position start, Position center, int range)
{
	Bfs(start);
	int width = game->map->sizeX, height = game->map->sizeY;
	int best_cost = INFINITY;
	Position best_target;

	for (int i = 0; i < width; i ++)
		for (int j = 0; j < height; j ++) {
			Position t(i, j);
			if (HamDistance(t, center) <= range && dist[t.x][t.y] < best_cost) {
				best_cost = dist[t.x][t.y];
				best_target = t;
			}
		}

	if (best_cost == INFINITY)
	{
		LOG << "Can't reach specific region" << endl;
		return false;
	}

	vector<Position> road;
	Position u = best_target;
	while (!(u == start)) {
		road.push_back(u);
		u = prev[u.x][u.y];
	}
	road.push_back(start);

	for (int i = road.size() - 1; i > 0; i --) {
		for (int j = 0; j < 4; j ++) {
			if (road[i].x + dx[j] == road[i - 1].x && road[i].y + dy[j] == road[i - 1].y) {
				ActionMove* am = new ActionMove();
				am->d = (Direction)(j);
				am->game = game;
				am->source = GetCntArmy();
				ret.push_back(am);
				break;
			}
		}
	}
	return true;
}

bool UserAPI::AttackArmy(vector<ActionBase*>& ret, Position starting, Army* enemy) {
	int width = game->map->sizeX, height = game->map->sizeY;
	Position best(-1, -1);
	int min = INFINITY;
	for (int i = 0; i < width; i ++)
		for (int j = 0; j < height; j ++) {
			Position t(i, j);
			if ( !(t == enemy->pos) && HamDistance(t, enemy->pos) <= SOLDIER_ATTACK_RANGE[GetCntArmy()->soldierType] ) {
				Bfs(starting);
				if (dist[t.x][t.y] < min) {
					min = dist[t.x][t.y];
					best = t;
				}
			}
		}
	if (min == INFINITY) return false;
	Move(ret, starting, best);
	ActionAttackArmy* aaa = new ActionAttackArmy();
	aaa->target = enemy;
	aaa->source = GetCntArmy();
	aaa->game = game;
	ret.push_back(aaa);
	return true;
}

bool UserAPI::BuildBarrack(vector<ActionBase*>& ret, LandCity *ncity) {
	LandCity* city = ncity ? ncity : GetCity(GetCntArmy()->pos);
	if (city == NULL) return false;
	if (city->hasBarrack) return false;
	ActionBuildBarrack* abb = new ActionBuildBarrack();
	abb->source = GetCntArmy();
	abb->game = game;
	ret.push_back(abb);
	return true;
}

bool UserAPI::RepairCity(vector<ActionBase*>& ret, LandCity *ncity, int amount) {
	LandCity* city = ncity ? ncity : GetCity(GetCntArmy()->pos);
	if (city != NULL) {
		ActionRepair* ar = new ActionRepair();
		ar->amount = amount;
		if (ar->amount * REPAIR_MONEY > game->players[user_id].money)
			ar->amount = game->players[user_id].money / REPAIR_MONEY;
		ar->source = GetCntArmy();
		ar->game = game;
		ret.push_back(ar);
		return true;
	}
	return false;
}

Army *UserAPI::GetMyArmy(GeneralType general)
{
	for (int i = 0; i < (int) my_army.size(); ++i)
		if (my_army[i]->general->type == general)
			return my_army[i];
	return 0;
}

Army *UserAPI::GetOpArmy(GeneralType general)
{
	for (int i = 0; i < (int) op_army.size(); ++i)
		if (op_army[i]->general->type == general)
			return op_army[i];
	return 0;
}

bool UserAPI::InDanger(Position pos, int range)
{
	for (int i = 0; i < (int) op_army.size(); ++i)
		if (this->HamDistance(op_army[i]->pos, pos) <= range)
			return true;
	return false;
}

vector<Army*> UserAPI::GetOpArmyAround(Position pos, int range)
{
	vector<Army*> ret;
	for (int i = 0; i < (int) op_army.size(); ++i)
		if (this->HamDistance(op_army[i]->pos, pos) <= range)
			ret.push_back(op_army[i]);
	return ret;
}

vector<Army*> UserAPI::GetMyArmyAround(Position pos, int range)
{
	vector<Army*> ret;
	for (int i = 0; i < (int) my_army.size(); ++i)
		if (this->HamDistance(my_army[i]->pos, pos) <= range)
			ret.push_back(my_army[i]);
	return ret;
}

Army* UserAPI::GetNearestArmy(Position pos, vector<Army*> &troop)
{
	int min_dist = INFINITY;
	Army *ret = NULL;
	for (int i = 0; i < (int) troop.size(); ++i)
		if (HamDistance(troop[i]->pos, pos) < min_dist)
		{
			min_dist = HamDistance(troop[i]->pos, pos);
			ret = troop[i];
		}

	return ret;
}

Army *UserAPI::GetArmy(GeneralType name)
{
	for (int i = 0; i < (int) op_army.size(); ++i)
		if (op_army[i]->general->type == name)
			return op_army[i];

	for (int i = 0; i < (int) my_army.size(); ++i)
		if (my_army[i]->general->type == name)
			return my_army[i];

	return NULL;
}

int UserAPI::GetArmyCount(vector<Army*> &troop)
{
	int ret = 0;
	for (int i = 0; i < (int) troop.size(); ++i)
		ret += troop[i]->numSoldier;
	return ret;
}

bool UserAPI::FindSafeWayTo(vector<ActionBase*>& ret, Position start, Position target, int my_army_count)
{
	//	TODO	fix me
	return Move(ret, start, target);
}

bool UserAPI::FindSafeWayToRegion(vector<ActionBase*>& ret, Position start, Position target, int my_army_count, int range)
{
	//	TODO	fix me
	return MoveToRegion(ret, start, target, range);
}

bool UserAPI::AttackCity(vector<ActionBase*>& ret, Position starting, LandCity* city)
{
	Position center = GetCityCenter(city);

	Bfs(starting);

	Position best(-1, -1), target;
	int min = INFINITY;

	{
		int dir_x[9] = {-1, -1, -1, 0, 0, 0, 1, 1, 1};
		int dir_y[9] = {-1, 0, 1, -1, 0, 1, -1, 0, 1};

		int width = game->map->sizeX;
		int height = game->map->sizeY;
		for (int i = 0; i < width; i ++)
			for (int j = 0; j < height; j ++) 
			{
				Position p(i, j);
				for (int k = 0; k < 9; ++k)
				{
					int x = center.x + dir_x[k];
					int y = center.y + dir_y[k];
					Position q(x, y);
					if (x >= 0 && x < width && y >= 0 && y < height
						&& HamDistance(p, q) <= SOLDIER_ATTACK_RANGE[GetCntArmy()->soldierType])
					{
						if (dist[i][j] < min)
						{
							min = dist[i][j];
							best = p;
							target = q;
						}
					}
				}
			}
	}
			
	Move(ret, starting, best);

	ActionAttackCity *ac = new ActionAttackCity();
	ac->source = GetCntArmy();
	ac->game = game;
	ac->targetPos = target;
	ret.push_back(ac);
	
	return true;
}
